home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / sunbird / js / calIcsParser.js < prev    next >
Encoding:
JavaScript  |  2007-05-23  |  9.2 KB  |  242 lines

  1. /* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  * ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is Mozilla Calendar code.
  16.  *
  17.  * The Initial Developer of the Original Code is
  18.  *   Michiel van Leeuwen <mvl@exedo.nl>.
  19.  * Portions created by the Initial Developer are Copyright (C) 2006
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. function calIcsParser() {
  39.     this.wrappedJSObject = this;
  40.     this.mItems = new Array();
  41.     this.mComponents = new Array();
  42.     this.mProperties = new Array();
  43. }
  44.  
  45. calIcsParser.prototype.QueryInterface =
  46. function QueryInterface(aIID) {
  47.     if (!aIID.equals(Components.interfaces.nsISupports) &&
  48.         !aIID.equals(Components.interfaces.calIIcsParser)) {
  49.         throw Components.results.NS_ERROR_NO_INTERFACE;
  50.     }
  51.  
  52.     return this;
  53. };
  54.  
  55. calIcsParser.prototype.parseString =
  56. function ip_parseString(aICSString) {
  57.     icsSvc = Components.classes["@mozilla.org/calendar/ics-service;1"]
  58.                        .getService(Components.interfaces.calIICSService);
  59.  
  60.     var rootComp = icsSvc.parseICS(aICSString);
  61.     var calComp;
  62.     // libical returns the vcalendar component if there is just one vcalendar.
  63.     // If there are multiple vcalendars, it returns an xroot component, with
  64.     // those vcalendar children. We need to handle both.
  65.     if (rootComp.componentType == 'VCALENDAR') {
  66.         calComp = rootComp;
  67.     } else {
  68.         calComp = rootComp.getFirstSubcomponent('VCALENDAR');
  69.     }
  70.  
  71.     var unexpandedItems = [];
  72.     var uid2parent = {};
  73.     var excItems = [];
  74.  
  75.     while (calComp) {
  76.  
  77.         // Get unknown properties
  78.         var prop = calComp.getFirstProperty("ANY");
  79.         while (prop) {
  80.             if (prop.propertyName != "VERSION" &&
  81.                 prop.propertyName != "PRODID") {
  82.                 this.mProperties.push(prop);
  83.             }
  84.             prop = calComp.getNextProperty("ANY");
  85.         }
  86.  
  87.         var prodId = calComp.getFirstProperty("PRODID");
  88.         var isFromOldSunbird;
  89.         if (prodId) {
  90.             isFromOldSunbird = prodId.value == "-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN";
  91.         }
  92.  
  93.         var subComp = calComp.getFirstSubcomponent("ANY");
  94.         while (subComp) {
  95.             var item = null;
  96.             switch (subComp.componentType) {
  97.             case "VEVENT":
  98.                 var event = Components.classes["@mozilla.org/calendar/event;1"]
  99.                                       .createInstance(Components.interfaces.calIEvent);
  100.                 item = event;
  101.                 break;
  102.             case "VTODO":
  103.                 var todo = Components.classes["@mozilla.org/calendar/todo;1"]
  104.                                      .createInstance(Components.interfaces.calITodo);
  105.                 item = todo;
  106.                 break;
  107.             case "VTIMEZONE":
  108.                 // this should already be attached to the relevant
  109.                 // events in the calendar, so there's no need to
  110.                 // do anything with it here.
  111.                 break;
  112.             default:
  113.                 this.mComponents.push(subComp);
  114.             }
  115.  
  116.             if (item) {
  117.                 item.icalComponent = subComp;
  118.  
  119.                 // Only try to fix ICS from Sunbird 0.2 (and earlier) if it
  120.                 // has an EXDATE.
  121.                 hasExdate = subComp.getFirstProperty("EXDATE");
  122.                 if (isFromOldSunbird && hasExdate) {
  123.                     item = fixOldSunbirdExceptions(item);
  124.                 }
  125.  
  126.                 var rid = item.recurrenceId;
  127.                 if (!rid) {
  128.                     unexpandedItems.push(item);
  129.                     if (item.recurrenceInfo) {
  130.                         uid2parent[item.id] = item;
  131.                     }
  132.                 } else {
  133.                     // force no recurrence info:
  134.                     item.recurrenceInfo = null;
  135.                     excItems.push(item);
  136.                 }
  137.             }
  138.             subComp = calComp.getNextSubcomponent("ANY");
  139.         }
  140.         calComp = rootComp.getNextSubcomponent("VCALENDAR");
  141.     }
  142.  
  143.     // tag "exceptions", i.e. items with rid:
  144.     for each (var item in excItems) {
  145.         var parent = uid2parent[item.id];
  146.         if (parent) {
  147.             item.parentItem = parent;
  148.             parent.recurrenceInfo.modifyException(item);
  149.         }
  150.     }
  151.     
  152.     for each (var item in unexpandedItems) {
  153.         this.mItems.push(item);
  154.     }
  155. };
  156.  
  157. calIcsParser.prototype.parseFromStream =
  158. function ip_parseFromStream(aStream) {
  159.     // Read in the string. Note that it isn't a real string at this point, 
  160.     // because likely, the file is utf8. The multibyte chars show up as multiple
  161.     // 'chars' in this string. So call it an array of octets for now.
  162.     
  163.     var octetArray = [];
  164.     var binaryIS = Components.classes["@mozilla.org/binaryinputstream;1"]
  165.                              .createInstance(Components.interfaces.nsIBinaryInputStream);
  166.     binaryIS.setInputStream(aStream);
  167.     octetArray = binaryIS.readByteArray(binaryIS.available());
  168.     
  169.    
  170.     // Some other apps (most notably, sunbird 0.2) happily splits an UTF8
  171.     // character between the octets, and adds a newline and space between them,
  172.     // for ICS folding. Unfold manually before parsing the file as utf8.This is
  173.     // UTF8 safe, because octets with the first bit 0 are always one-octet
  174.     // characters. So the space or the newline never can be part of a multi-byte
  175.     // char.
  176.     for (var i = octetArray.length - 2; i >= 0; i--) {
  177.         if (octetArray[i] == "\n" && octetArray[i+1] == " ") {
  178.             octetArray = octetArray.splice(i, 2);
  179.         }
  180.     }
  181.  
  182.     // Interpret the byte-array as a UTF8-string, and convert into a
  183.     // javascript string.
  184.     var unicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
  185.                                      .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  186.     // ICS files are always UTF8
  187.     unicodeConverter.charset = "UTF-8";
  188.     var str = unicodeConverter.convertFromByteArray(octetArray, octetArray.length);
  189.     return this.parseString(str);
  190. }
  191.  
  192. calIcsParser.prototype.getItems =
  193. function ip_getItems(aCount) {
  194.     aCount.value = this.mItems.length;
  195.     return this.mItems.concat([]); //clone
  196. }
  197.  
  198. calIcsParser.prototype.getProperties =
  199. function ip_getProperties(aCount) {
  200.     aCount.value = this.mProperties.length;
  201.     return this.mProperties.concat([]); //clone
  202. }
  203.  
  204. calIcsParser.prototype.getComponents =
  205. function ip_getComponents(aCount) {
  206.     aCount.value = this.mComponents.length;
  207.     return this.mComponents.concat([]); //clone
  208. }
  209.  
  210. // Helper function to deal with the busted exdates from Sunbird 0.2
  211. // When Sunbird 0.2 (and earlier) creates EXDATEs, they are set to
  212. // 00:00:00 floating rather than to the item's DTSTART. This fixes that.
  213. // (bug 354073)
  214. function fixOldSunbirdExceptions(aItem) {
  215.     const kCalIRecurrenceDate = Components.interfaces.calIRecurrenceDate;
  216.  
  217.     var item = aItem;
  218.     var ritems = aItem.recurrenceInfo.getRecurrenceItems({});
  219.     for each (var ritem in ritems) {
  220.         // EXDATEs are represented as calIRecurrenceDates, which are
  221.         // negative and finite.
  222.         if (ritem instanceof kCalIRecurrenceDate &&
  223.             ritem.isNegative &&
  224.             ritem.isFinite) {
  225.             // Only mess with the exception if its time is wrong.
  226.             var oldDate = aItem.startDate || aItem.entryDate;
  227.             if (ritem.date.compare(oldDate) != 0) {
  228.                 var newRitem = ritem.clone();
  229.                 // All we want from aItem is the time and timezone.
  230.                 newRitem.date.timezone = oldDate.timezone;
  231.                 newRitem.date.hour     = oldDate.hour;
  232.                 newRitem.date.minute   = oldDate.minute;
  233.                 newRitem.date.second   = oldDate.second;
  234.                 newRitem.date.normalize();
  235.                 item.recurrenceInfo.appendRecurrenceItem(newRitem);
  236.                 item.recurrenceInfo.deleteRecurrenceItem(ritem);
  237.             }
  238.         }
  239.     }
  240.     return item;
  241. }
  242.